﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Chess
{
    public class ChessGame
    {
        public ChessGame()
        {
            this.ChessBoard = new ChessBoard();

            for (int r = 0; r < ChessBoard.Rows; r++)
            {
                for (int c = 0; c < ChessBoard.Columns; c++)
                {
                    this.ChessBoard[r, c] = ChessCell.Empty;
                }
            }

            for (int c = 0; c < 8; c++)
            {
                this.ChessBoard[1, c] = ChessCell.WhitePawn;
                this.ChessBoard[6, c] = ChessCell.BlackPawn;
            }

            this.ChessBoard[0, 0] = ChessCell.WhiteRook;
            this.ChessBoard[0, 1] = ChessCell.WhiteKnight;
            this.ChessBoard[0, 2] = ChessCell.WhiteBishop;
            this.ChessBoard[0, 3] = ChessCell.WhiteKing;
            this.ChessBoard[0, 4] = ChessCell.WhiteQueen;
            this.ChessBoard[0, 5] = ChessCell.WhiteBishop;
            this.ChessBoard[0, 6] = ChessCell.WhiteKnight;
            this.ChessBoard[0, 7] = ChessCell.WhiteRook;

            this.ChessBoard[7, 0] = ChessCell.BlackRook;
            this.ChessBoard[7, 1] = ChessCell.BlackKnight;
            this.ChessBoard[7, 2] = ChessCell.BlackBishop;
            this.ChessBoard[7, 3] = ChessCell.BlackKing;
            this.ChessBoard[7, 4] = ChessCell.BlackQueen;
            this.ChessBoard[7, 5] = ChessCell.BlackBishop;
            this.ChessBoard[7, 6] = ChessCell.BlackKnight;
            this.ChessBoard[7, 7] = ChessCell.BlackRook;
        }

        public ChessBoard ChessBoard;

        public delegate void ChessBoardChangedEventHandler();
        public event ChessBoardChangedEventHandler ChessBoardChanged;

        private void RaiseChessBoardChanged()
        {
            ChessBoardChangedEventHandler copyChessBoardChanged = this.ChessBoardChanged;

            if (copyChessBoardChanged != null)
            {
                copyChessBoardChanged();
            }
        }

        public bool TryMakeMove(CellCoordinates from, CellCoordinates to)
        {
            if (this.ChessBoard[from.Row, from.Column] == ChessCell.Empty)
            {
                return false;
            }

            var validCells = GetValidMoveCellCoordinates(from);

            if (!validCells.Contains(to))
            {
                return false;
            }

            this.ChessBoard[to.Row, to.Column] = this.ChessBoard[from.Row, from.Column];
            this.ChessBoard[from.Row, from.Column] = ChessCell.Empty;            
            
            return true;
        }

        public bool TryMakeMove(int rowFrom, int columnFrom, int rowTo, int columnTo)
        {
            return this.TryMakeMove(new CellCoordinates(rowFrom, columnFrom), new CellCoordinates(rowTo, columnTo));
        }

        public List<CellCoordinates> GetValidMoveCellCoordinates(CellCoordinates cellCoordinates)
        {            
            var validCells =  new List<CellCoordinates>();

            for (int r = 0; r < ChessBoard.Rows; r++)
			{
                for (int c = 0; c < ChessBoard.Columns; c++)
			    {
                    if (this.IsValidMove(cellCoordinates, r, c))
                    { 
                        validCells.Add(new CellCoordinates(r, c));
                    }
	    		}
			}

            return validCells;
        }

        private bool IsValidMove(CellCoordinates from, int rowTo, int columnTo)
        {
            if (!this.IsInsideChessBoard(rowTo, columnTo))
            {
                return false;
            }

            ChessCell cell = this.ChessBoard[from.Row, from.Column];

            bool isValidMove_Piece = false;

            if (cell == ChessCell.Empty)
            {
                return false;
            }

            if (cell == ChessCell.WhiteKing || cell == ChessCell.BlackKing)
            {
                isValidMove_Piece = this.IsValidMove_King(from, rowTo, columnTo);
            }
            else if (cell == ChessCell.WhiteQueen || cell == ChessCell.BlackQueen)
            {
                isValidMove_Piece = this.IsValidMove_Queen(from, rowTo, columnTo);
            }
            else if (cell == ChessCell.WhiteRook || cell == ChessCell.BlackRook)
            {
                isValidMove_Piece = this.IsValidMove_Rook(from, rowTo, columnTo);
            }
            else if (cell == ChessCell.WhiteKnight || cell == ChessCell.BlackKnight)
            {
                isValidMove_Piece = this.IsValidMove_Knight(from, rowTo, columnTo);
            }
            else if (cell == ChessCell.WhiteBishop || cell == ChessCell.BlackBishop)
            {
                isValidMove_Piece = this.IsValidMove_Bishop(from, rowTo, columnTo);
            }
            else if (cell == ChessCell.WhitePawn)
            {
                isValidMove_Piece = this.IsValidMove_WhitePawn(from, rowTo, columnTo);
            }
            else if (cell == ChessCell.BlackPawn)
            {
                isValidMove_Piece = this.IsValidMove_BlackPawn(from, rowTo, columnTo);
            }
            else
            {
                throw new Exception("Unrecognized figure.");
            }

            bool isValidMove = isValidMove_Piece 
                &&(this.IsEmpty(rowTo, columnTo) || this.AreDifferentColors(from, rowTo, columnTo));

            return isValidMove;
        }        

        private bool IsValidMove_King(CellCoordinates from, int rowTo, int columnTo)
        {
            return (from.Row - 1 == rowTo && from.Column - 1 == columnTo) ||
                (from.Row - 1 == rowTo && from.Column == columnTo) ||
                (from.Row - 1 == rowTo && from.Column + 1 == columnTo) ||
                (from.Row == rowTo && from.Column - 1 == columnTo) ||
                (from.Row == rowTo && from.Column + 1 == columnTo) ||
                (from.Row + 1 == rowTo && from.Column -1 == columnTo) ||
                (from.Row + 1 == rowTo && from.Column == columnTo) ||
                (from.Row + 1 == rowTo && from.Column + 1 == columnTo); 
        }

        private bool IsValidMove_Queen(CellCoordinates from, int rowTo, int columnTo)
        {
            return (from.Row - from.Column == rowTo - columnTo) ||
                (from.Row + from.Column == rowTo + columnTo) ||
                (from.Row == rowTo && from.Column != columnTo) ||
                (from.Row != rowTo && from.Column == columnTo);
        }

        private bool IsValidMove_Knight(CellCoordinates from, int rowTo, int columnTo)
        {
            return (from.Row - 1 == rowTo && from.Column - 2 == columnTo) ||
                (from.Row + 1 == rowTo && from.Column - 2 == columnTo) ||
                (from.Row - 1 == rowTo && from.Column + 2 == columnTo) ||
                (from.Row + 1 == rowTo && from.Column + 2 == columnTo) ||
                (from.Row - 2 == rowTo && from.Column - 1 == columnTo) ||
                (from.Row + 2 == rowTo && from.Column - 1 == columnTo) ||
                (from.Row - 2 == rowTo && from.Column + 1 == columnTo) ||
                (from.Row + 2 == rowTo && from.Column + 1 == columnTo);
        }

        private bool IsValidMove_Bishop(CellCoordinates from, int rowTo, int columnTo)
        {
            return (from.Row - from.Column == rowTo - columnTo) ||
                (from.Row + from.Column == rowTo + columnTo);
        }

        private bool IsValidMove_Rook(CellCoordinates from, int rowTo, int columnTo)
        {
            bool isValidMove_Rook = (from.Row == rowTo && from.Column != columnTo) ||
                (from.Row != rowTo && from.Column == columnTo);

            if (!isValidMove_Rook)
            {
                return false;
            }

            for (int r = from.Row + 1; r < rowTo; r++)
            {
                if (!this.IsInsideAndEmpty(r, columnTo))
                {
                    return false;
                }
            }

            for (int r = from.Row - 1; r > rowTo; r--)
            {
                if (!this.IsInsideAndEmpty(r, columnTo))
                {
                    return false;
                }
            }

            for (int c = from.Column + 1; c < columnTo; c++)
            {
                if (!this.IsInsideAndEmpty(rowTo, c))
                {
                    return false;
                }
            }

            for (int c = from.Column -1; c > columnTo; c--)
            {
                if (!this.IsInsideAndEmpty(rowTo, c))
                {
                    return false;
                }
            }

            return true;
        }

        private bool IsValidMove_WhitePawn(CellCoordinates from, int rowTo, int columnTo)
        {
            bool isRegularMove = (from.Row + 1 == rowTo) && (from.Column == columnTo)
                && this.IsEmpty(rowTo, columnTo);

            if (isRegularMove)
            {
                return true;
            }

            bool isFirstMove = (from.Row == 1) &&
                (from.Row + 2 == rowTo) && (from.Column == columnTo)
                && this.IsEmpty(rowTo, columnTo);

            if (isFirstMove)
            {
                return true;
            }

            bool isTakeMove = from.Row + 1 == rowTo &&
                ((from.Column - 1 == columnTo) || (from.Column + 1 == columnTo)) && 
                this.AreDifferentColors(from, rowTo, columnTo);

            return isTakeMove;
        }

        private bool IsValidMove_BlackPawn(CellCoordinates from, int rowTo, int columnTo)
        {
            return (from.Row - 1 == rowTo &&
                (from.Column - 1 == columnTo || from.Column + 1 == columnTo || 
                    (from.Column == columnTo && this.IsInsideAndEmpty(rowTo, columnTo)))) ||
                ((from.Row - 2 == rowTo && from.Column == columnTo) &&
                    IsInsideAndEmpty(from.Row - 1, from.Column));
        }

        private bool IsInsideChessBoard(int row, int column)
        {
            return row >= 0 && row <= ChessBoard.Rows &&
                column >= 0 && column <= ChessBoard.Columns;
        }

        private bool AreDifferentColors(CellCoordinates from, int rowTo, int columnTo)
        {
            bool fromIsWhite = this.IsWhite(from.Row, from.Column);
            bool fromIsBlack = this.IsBlack(from.Row, from.Column);

            bool toIsWhite = this.IsWhite(from.Row, from.Column);
            bool toIsBlack = this.IsBlack(from.Row, from.Column);

            return (fromIsWhite && toIsBlack) ||
                (fromIsBlack && toIsWhite);
        }

        private bool IsWhite(int row, int column)
        {
            return this.ChessBoard[row, column] == ChessCell.WhiteKing ||
                this.ChessBoard[row, column] == ChessCell.WhiteQueen ||
                this.ChessBoard[row, column] == ChessCell.WhiteRook ||
                this.ChessBoard[row, column] == ChessCell.WhiteKnight ||
                this.ChessBoard[row, column] == ChessCell.WhiteBishop ||
                this.ChessBoard[row, column] == ChessCell.WhitePawn;
        }

        private bool IsBlack(int row, int column)
        {
            return this.ChessBoard[row, column] == ChessCell.BlackKing ||
                this.ChessBoard[row, column] == ChessCell.BlackQueen ||
                this.ChessBoard[row, column] == ChessCell.BlackRook ||
                this.ChessBoard[row, column] == ChessCell.BlackKnight ||
                this.ChessBoard[row, column] == ChessCell.BlackBishop ||
                this.ChessBoard[row, column] == ChessCell.BlackPawn;
        }

        private bool IsEmpty(int row, int column)
        {
            return this.ChessBoard[row, column] == ChessCell.Empty;
        }

        private bool IsInsideAndEmpty(int row, int column)
        {
            return this.IsInsideChessBoard(row, column) 
                && this.IsEmpty(row, column);
        }
    }
}
